﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using MiscUtil.Conversion;

namespace FrmConverter
{
	public sealed class FrmConverter
	{
		static MemoryStream _buffer;
		static int _id;
		static int _numberOfFramePointers;
		static int _numberOfFrames;

		static byte[] bmp_header = {0x42,0x4D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x04,0x00,0x00,0x28,0x00
			,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x08,0x00,0x00,0x00
			,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01
			,0x00,0x00,0x00,0x01,0x00,0x00,0x0B,0x00,0x0B,0x00,0x41,0x40,0x91,0x00,0x00,0xA2
			,0x00,0x00,0x00,0xA1,0xA2,0x00,0xD1,0xD0,0xD1,0x00,0xC9,0xC7,0xC9,0x00,0xBC,0xBB
			,0xBC,0x00,0xB0,0xAE,0xB0,0x00,0xA3,0xA1,0xA3,0x00,0x99,0x97,0x99,0x00,0x8B,0x89
			,0x8B,0x00,0x7C,0x7A,0x7C,0x00,0x70,0x6E,0x70,0x00,0x5F,0x5D,0x5F,0x00,0x4D,0x4A
			,0x4D,0x00,0x42,0x40,0x42,0x00,0xF3,0xF2,0xFE,0x00,0xE5,0xE5,0xF3,0x00,0xD6,0xD6
			,0xE8,0x00,0xC8,0xC8,0xE0,0x00,0xBC,0xBC,0xD4,0x00,0xAF,0xAF,0xC9,0x00,0xA1,0xA2
			,0xBF,0x00,0x94,0x94,0xB3,0x00,0x85,0x86,0xA6,0x00,0x7A,0x7B,0x9C,0x00,0x6A,0x6B
			,0x8E,0x00,0x5E,0x5E,0x80,0x00,0x50,0x50,0x74,0x00,0x45,0x46,0x64,0x00,0x36,0x36
			,0x51,0x00,0x2B,0x29,0x42,0x00,0xFE,0xF2,0xF2,0x00,0xF3,0xE4,0xE5,0x00,0xE8,0xD6
			,0xD7,0x00,0xE0,0xC8,0xC8,0x00,0xD4,0xBC,0xBC,0x00,0xC9,0xAF,0xAF,0x00,0xBF,0xA1
			,0xA2,0x00,0xB3,0x94,0x94,0x00,0xA6,0x85,0x86,0x00,0x9C,0x7A,0x7B,0x00,0x8E,0x6A
			,0x6B,0x00,0x80,0x5E,0x5E,0x00,0x74,0x50,0x50,0x00,0x64,0x46,0x48,0x00,0x51,0x34
			,0x36,0x00,0x42,0x29,0x29,0x00,0xF6,0xC8,0xFD,0x00,0xC3,0x86,0xD6,0x00,0x86,0x46
			,0x8D,0x00,0x6F,0x31,0x73,0x00,0x5A,0x22,0x5E,0x00,0x45,0x29,0x4B,0x00,0x45,0x16
			,0x46,0x00,0x36,0x20,0x3D,0x00,0xEE,0xF8,0xF8,0x00,0xD7,0xEE,0xEF,0x00,0x90,0xCF
			,0xD9,0x00,0x73,0xC3,0xD2,0x00,0x51,0xB7,0xCC,0x00,0x55,0xA8,0xBF,0x00,0x46,0x9C
			,0xAF,0x00,0x36,0x8D,0x9F,0x00,0x29,0x80,0x91,0x00,0x1A,0x6E,0x7F,0x00,0x11,0x5E
			,0x70,0x00,0x00,0x4B,0x5A,0x00,0x00,0x37,0x41,0x00,0xD4,0xF5,0xDC,0x00,0xA9,0xE5
			,0xCB,0x00,0x94,0xCE,0xB5,0x00,0x83,0xB6,0x9B,0x00,0x6E,0x9B,0x81,0x00,0x5B,0x7F
			,0x67,0x00,0x40,0x5E,0x4A,0x00,0x77,0x86,0x94,0x00,0x58,0x6F,0x7B,0x00,0x41,0x55
			,0x5F,0x00,0x77,0x9B,0x8E,0x00,0x42,0x9B,0x95,0x00,0x4B,0x8D,0x94,0x00,0x48,0x86
			,0x85,0x00,0x46,0x6B,0x73,0x00,0x41,0x55,0x5F,0x00,0xB8,0xC5,0xB9,0x00,0x9A,0xB1
			,0x9B,0x00,0x80,0x9F,0x7E,0x00,0x66,0x8C,0x67,0x00,0x7F,0x7E,0x5E,0x00,0x6E,0x73
			,0x55,0x00,0x62,0x6B,0x4B,0x00,0x50,0x62,0x41,0x00,0x48,0x56,0x3C,0x00,0x36,0x4B
			,0x30,0x00,0x28,0x41,0x29,0x00,0x37,0x55,0x34,0x00,0x22,0x46,0x28,0x00,0x11,0x3C
			,0x18,0x00,0x00,0x30,0x11,0x00,0x00,0x22,0x0E,0x00,0xB8,0xB8,0xAC,0x00,0xB5,0xB2
			,0x9B,0x00,0xB3,0xA9,0x8B,0x00,0xAF,0x9F,0x77,0x00,0xAC,0x90,0x67,0x00,0xAC,0x7F
			,0x55,0x00,0x9F,0x73,0x51,0x00,0x90,0x6B,0x4A,0x00,0x83,0x5E,0x42,0x00,0x73,0x55
			,0x3A,0x00,0x67,0x4B,0x37,0x00,0xBF,0xBF,0xB9,0x00,0x8D,0x6F,0x5E,0x00,0x80,0x7F
			,0x78,0x00,0xA5,0x8D,0x7E,0x00,0x77,0x67,0x5D,0x00,0xD1,0xD0,0xD1,0x00,0xB5,0xBF
			,0xC4,0x00,0xA0,0xB0,0xBC,0x00,0x85,0x9F,0xB2,0x00,0x72,0x8C,0xA9,0x00,0x5A,0x80
			,0x9F,0x00,0x48,0x6F,0x94,0x00,0x30,0x61,0x8A,0x00,0x1A,0x54,0x7F,0x00,0xDB,0xDB
			,0xFD,0x00,0xC9,0xC8,0xFE,0x00,0xB6,0xB5,0xFE,0x00,0x9F,0x9E,0xFD,0x00,0x8A,0x89
			,0xFD,0x00,0x6F,0x6F,0xFE,0x00,0x6B,0x6B,0xF4,0x00,0x66,0x64,0xE2,0x00,0x5C,0x5C
			,0xD0,0x00,0x55,0x54,0xC0,0x00,0x4B,0x4D,0xAD,0x00,0x44,0x44,0x9C,0x00,0x3D,0x3C
			,0x89,0x00,0x33,0x31,0x71,0x00,0x28,0x28,0x5C,0x00,0xDB,0xE9,0xFC,0x00,0xB9,0xD5
			,0xFB,0x00,0xA9,0xCB,0xFA,0x00,0x96,0xC0,0xF9,0x00,0x81,0xB5,0xFA,0x00,0x6D,0xAC
			,0xF8,0x00,0x58,0xA1,0xF7,0x00,0x3E,0x97,0xF6,0x00,0x0B,0x91,0xE7,0x00,0x00,0x87
			,0xD4,0x00,0x00,0x77,0xBF,0x00,0x00,0x6B,0xA5,0x00,0x07,0x5A,0x8E,0x00,0x07,0x46
			,0x73,0x00,0x00,0x36,0x54,0x00,0xC7,0xDF,0xF8,0x00,0x9C,0xC8,0xE5,0x00,0x8A,0xBC
			,0xD9,0x00,0x7B,0xAF,0xD0,0x00,0x6B,0xA2,0xC4,0x00,0x5B,0x98,0xB9,0x00,0x4B,0x8A
			,0xAB,0x00,0x3A,0x7F,0x9F,0x00,0x2E,0x73,0x94,0x00,0x1A,0x67,0x86,0x00,0x11,0x5A
			,0x77,0x00,0x00,0x4B,0x68,0x00,0x00,0x41,0x5A,0x00,0xD9,0xE9,0xF9,0x00,0xB6,0xD9
			,0xEF,0x00,0x9F,0xC5,0xE1,0x00,0x8B,0xAF,0xD6,0x00,0x72,0x98,0xC8,0x00,0x5E,0x82
			,0xBB,0x00,0x50,0x73,0xAF,0x00,0x40,0x62,0xA5,0x00,0x36,0x50,0x9A,0x00,0x2E,0x42
			,0x91,0x00,0x28,0x3A,0x7F,0x00,0x20,0x31,0x69,0x00,0x20,0x29,0x5A,0x00,0xE7,0xF0
			,0xFD,0x00,0xD0,0xE2,0xFA,0x00,0xBC,0xD4,0xF8,0x00,0xA5,0xC8,0xF5,0x00,0x91,0xBC
			,0xF5,0x00,0x82,0xB2,0xF5,0x00,0x7B,0xA2,0xE5,0x00,0x6F,0x94,0xD4,0x00,0x66,0x86
			,0xC2,0x00,0x5E,0x77,0xAE,0x00,0x55,0x67,0x9B,0x00,0x45,0x55,0x85,0x00,0x3A,0x46
			,0x6F,0x00,0x30,0x36,0x5E,0x00,0x8A,0xED,0x8A,0x00,0x30,0xB5,0x2E,0x00,0x00,0xBF
			,0x00,0x00,0x70,0x77,0x77,0x00,0x00,0x91,0x00,0x00,0xA6,0xAC,0xAC,0x00,0x3D,0x3A
			,0x3D,0x00,0x5D,0x77,0x8C,0x00,0x40,0x4B,0x54,0x00,0x87,0x94,0xAD,0x00,0x4B,0x5E
			,0x6F,0x00,0x24,0x20,0x24,0x00,0x64,0x61,0x64,0x00,0x92,0x98,0x91,0x00,0x9C,0xA5
			,0x9A,0x00,0xA9,0xB2,0xA8,0x00,0xB1,0xBF,0xB2,0x00,0x85,0x8D,0x7E,0x00,0x8C,0x94
			,0x85,0x00,0x57,0xEA,0x56,0x00,0x36,0xDF,0x58,0x00,0x29,0xCB,0x5A,0x00,0x30,0xB3
			,0x56,0x00,0x36,0x98,0x4B,0x00,0xFE,0xFC,0xFE,0x00,0xE1,0xF2,0xF4,0x00,0xA8,0xCE
			,0xDF,0x00,0x77,0x9F,0xB5,0x00,0x64,0x7F,0x8E,0x00,0x48,0x67,0x78,0x00,0x3A,0x4B
			,0x58,0x00,0x22,0x29,0x36,0x00,0x0B,0x00,0x0B,0x00,0x36,0xA2,0x4B,0x00,0x00,0x91
			,0x00,0x00,0x11,0x95,0x1A,0x00,0x24,0x9B,0x36,0x00,0x91,0x8C,0x8C,0x00,0x9F,0x8A
			,0x87,0x00,0xAC,0x8D,0x7B,0x00,0xBC,0xAF,0x00,0x00,0xFD,0xCE,0x8D,0x00,0x66,0x64
			,0xE2,0x00,0x5B,0x58,0xC9,0x00,0x26,0x51,0xAB,0x00,0x36,0x92,0xF9,0x00,0x31,0x75
			,0xF3,0x00,0x3C,0x3C,0x8B,0x00,0x4F,0x4F,0xB2,0x00,0x3C,0x3C,0x8B,0x00,0x2B,0x2B
			,0x60,0x00,0x2B,0x2B,0x60,0x00,0x4B,0x5E,0x6F,0x00,0x45,0x5A,0x68,0x00,0x45,0x55
			,0x61,0x00,0x41,0x50,0x58,0x00,0x40,0x4B,0x54,0x00,0x4B,0x62,0x77,0x00,0x53,0x54
			,0xBD,0x00,0x0B,0x00,0x0B,0x00};
			

		public static Frame GetImage(byte[] imageData)
		{
			MemoryStream buffer = new MemoryStream(imageData);

			_numberOfFrames = 1;

			Frame frame = new Frame(0);
			frame.SizeX = GetWordValue(buffer, 62 + frame.Offset);
			frame.SizeY = GetWordValue(buffer, 62 + frame.Offset + 2);
			frame.FrameLength = GetDwordValue(buffer, 62 + frame.Offset + 4);

			var alignedWidth = (4 - frame.SizeX % 4) % 4;
			
			for (int i = 0; i < frame.SizeY; i++)
			{
				frame.FrameData = ConcatArray(frame.FrameData, GetBytes(buffer, 62 + frame.Offset + 12 + (frame.FrameLength - frame.SizeX * i), frame.SizeX));
				for (int k = 0; k<alignedWidth; k++)
					frame.FrameData = ConcatArray(frame.FrameData, new byte[] { 0x00 });
			}

			byte[] bmp = ConcatArray(bmp_header, frame.FrameData);

			MemoryStream ms = new MemoryStream(bmp, 0, bmp.Length);

			byte[] x = BitConverter.GetBytes(frame.SizeX);
			byte[] y = BitConverter.GetBytes(frame.SizeY);

			ms.Write(bmp, 0, bmp.Length);
			ms.Position = 18;
			ms.Write(x, 0, 4);
			ms.Position = 22;
			ms.Write(y, 0, 4);
			ms.Position = 0;

			Image newImage = new Bitmap(ms);

			frame.FrameImage = newImage;

			return frame;
		}

		public static List<Frame> GetImages(byte[] imageData)
		{
			MemoryStream buffer = new MemoryStream(imageData);
			List<Frame> _frames = new List<Frame> { };

			_id = GetDwordValue(buffer, 0);
			int numberOfFramesPerDirection = GetWordValue(buffer, 4);

//			_numberOfFrames = GetDwordValue(buffer, 6);

			int frameNum = 0;
			int currentFrameOffset = 62;

			do
			{
				Frame frame = new Frame(currentFrameOffset);

				frame.SizeX = GetWordValue(buffer, frame.Offset);
				frame.SizeY = GetWordValue(buffer, frame.Offset + 2);
				frame.FrameLength = GetDwordValue(buffer, frame.Offset + 4);
				frame.XOffset = GetWordValue(buffer, frame.Offset + 8);
				frame.YOffset = GetWordValue(buffer, frame.Offset + 10);

				var alignedWidth = (4 - frame.SizeX % 4) % 4;

				var currentOffset = frame.Offset + 12 + frame.SizeX*(frame.SizeY - 1);

				for (int i = 0; i < frame.SizeY; i++)
				{
					frame.FrameData = ConcatArray(frame.FrameData, GetBytes(buffer, currentOffset, frame.SizeX));
					for (int k = 0; k < alignedWidth; k++)
						frame.FrameData = ConcatArray(frame.FrameData, new byte[] { 0x00 });
					currentOffset -= frame.SizeX;
				}

				_frames.Add(frame);

				currentFrameOffset += 12 + frame.FrameLength;

			} while (imageData.Length - currentFrameOffset > 12);

			for (int i = 0; i < _frames.Count; i++ )
			{
				Frame frame = _frames[i];
				byte[] bmp = ConcatArray(bmp_header, frame.FrameData);

				MemoryStream ms = new MemoryStream(bmp, 0, bmp.Length);

				byte[] x = BitConverter.GetBytes(frame.SizeX);
				byte[] y = BitConverter.GetBytes(frame.SizeY);

				ms.Write(bmp, 0, bmp.Length);
				ms.Position = 18;
				ms.Write(x, 0, 4);
				ms.Position = 22;
				ms.Write(y, 0, 4);
				ms.Position = 0;

				Image newImage = new Bitmap(ms);
				_frames[i].FrameImage = newImage;
			}

			return _frames;
		}

		private static byte[] ConcatArray(byte[] array1, byte[] array2)
		{
			byte[] result;
			if (array1 == null)
			{
				result = new byte[array2.Length];
				array2.CopyTo(result, 0);
			}
			else
			{
				result = new byte[array1.Length + array2.Length];
				array1.CopyTo(result, 0);
				array2.CopyTo(result, array1.Length);
			}

			return result;
		}

		private static int GetDwordValue(MemoryStream buffer, int offset)
		{
			byte[] result = new byte[4];

			buffer.Seek(offset, SeekOrigin.Begin);
			buffer.Read(result, 0, 4);

			return EndianBitConverter.Big.ToInt32(result, 0);
		}

		private static byte[] ReverseArray(byte[] data)
		{
			byte[] result = new byte[data.Length];

			for (int i = 0; i < data.Length; i++)
			{
				result[i] = data[data.Length - i - 1];
			}

			return result;
		}

		private static int GetWordValue(MemoryStream buffer, int offset)
		{
			byte[] result = new byte[2];

			buffer.Seek(offset, SeekOrigin.Begin);
			buffer.Read(result, 0, 2);

			return EndianBitConverter.Big.ToInt16(result, 0);
		}

		private static int GetByteValue(MemoryStream buffer, int offset)
		{
			byte[] result = new byte[1];

			buffer.Seek(offset, SeekOrigin.Begin);
			buffer.Read(result, 0, 1);

			return result[0];
		}

		private static byte[] GetBytes(MemoryStream buffer, int offset, int count)
		{
			byte[] result = new byte[count];

			buffer.Seek(offset, SeekOrigin.Begin);
			buffer.Read(result, 0, count);

			return result;
		}
	}
}
